﻿using System;
using System.Linq;
using Castle.Core;
using Castle.MicroKernel;
using Castle.MicroKernel.Context;
using Castle.MicroKernel.Handlers;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.Resolvers.SpecializedResolvers;
using Castle.Windsor;
using NUnit.Framework;

namespace WindsorInitConfig {
    [TestFixture]
    public class ResolveAllWithConstraints {
        [Test]
        public void ResolveAll_Extension() {
            var container = new WindsorContainer();

            container.Register(Component.For(typeof (IIface<,>)).ImplementedBy(typeof (HandlerImpl<,>)));
            container.Kernel.Resolver.AddSubResolver(new ArrayResolver(container.Kernel));
            var normal = container.ResolveAll<IIface<Impl2, Stub>>();
            var ex = container.TryResolveAll<IIface<Impl1, Stub>>();
            Assert.AreEqual(0, ex.Length);
        }

        [Test]
        public void ResolveAll_CustomHandler() {
            var container = new WindsorContainer();

            container.Register(Component.For(typeof (IIface<,>)).ImplementedBy(typeof (HandlerImpl<,>)));
            container.Register(Component.For<SomeService>());
            container.Kernel.Resolver.AddSubResolver(new ArrayResolver(container.Kernel));
            var normal = container.ResolveAll<IIface<Impl2, Stub>>();
            var s = container.Resolve<SomeService>();
        }

        public class CustomHandlerFactory : DefaultHandlerFactory {
            public CustomHandlerFactory(IKernel kernel) : base(kernel) {}
        }

        public class CustomKernel : DefaultKernel {}

        [Test]
        public void ResolveAll_CustomHandlerSelector() {
            var container = new WindsorContainer();
            container.Kernel.AddHandlerSelector(new CustomHandlerSelector());

            container.Register(Component.For(typeof (IIface<,>)).ImplementedBy(typeof (HandlerImpl<,>)));
            container.Register(Component.For<SomeService>());
            container.Kernel.Resolver.AddSubResolver(new ArrayResolver(container.Kernel));
            var normal = container.ResolveAll<IIface<Impl2, Stub>>();
            var s = container.Resolve<SomeService>();
        }

        [Test]
        public void ResolveAll_CustomResolver() {
            var container = new WindsorContainer();

            container.Register(Component.For(typeof (IIface<,>)).ImplementedBy(typeof (HandlerImpl<,>)));
            container.Register(Component.For<SomeService>());
            container.Kernel.Resolver.AddSubResolver(new CustomArrayResolver(container.Kernel, typeof(IIface<,>)));
            container.Kernel.Resolver.AddSubResolver(new ArrayResolver(container.Kernel));
            var normal = container.ResolveAll<IIface<Impl2, Stub>>();
            Assert.Greater(normal.Length, 0);
            var s = container.Resolve<SomeService>();
            Assert.AreEqual(0, s.s.Length);
        }

        public class CustomArrayResolver : ISubDependencyResolver {
            private readonly IKernel kernel;
            private readonly Type serviceTypeDefinition;

            public CustomArrayResolver(IKernel kernel, Type serviceTypeDefinition) {
                this.kernel = kernel;
                this.serviceTypeDefinition = serviceTypeDefinition;
            }

            private bool MatchesConstraints(Type service, Type impl) {
                try {
                    impl.MakeGenericType(service.GetGenericArguments());
                    return true;
                } catch (ArgumentException) {
                    return false;
                }
            }

            public object Resolve(CreationContext context, ISubDependencyResolver contextHandlerResolver,
                                  ComponentModel model,
                                  DependencyModel dependency) {
                var service = dependency.TargetType.GetElementType();
                var handlers = kernel.GetAssignableHandlers(service);
                var components = handlers
                    .Where(h => h.CurrentState == HandlerState.Valid)
                    .Where(h => MatchesConstraints(service, h.ComponentModel.Implementation))
                    .Select(h => h.Resolve(context, contextHandlerResolver, model, dependency))
                    .ToArray();
                var r = Array.CreateInstance(service, components.Length);
                components.CopyTo(r, 0);
                return r;
            }


            public bool CanResolve(CreationContext context, ISubDependencyResolver contextHandlerResolver,
                                   ComponentModel model,
                                   DependencyModel dependency) {
                return dependency.TargetType != null &&
                       dependency.TargetType.IsArray &&
                       kernel.HasComponent(dependency.TargetType.GetElementType()) && 
                       dependency.TargetType.GetElementType().IsGenericType &&
                       dependency.TargetType.GetElementType().GetGenericTypeDefinition() == serviceTypeDefinition;
            }
        }


        public class CustomHandlerSelector : IHandlerSelector {
            public bool HasOpinionAbout(string key, Type service) {
                Console.WriteLine(service);
                return service.IsGenericType && service.GetGenericTypeDefinition() == typeof (IIface<,>);
            }

            public IHandler SelectHandler(string key, Type service, IHandler[] handlers) {
                throw new NotImplementedException();
            }
        }

        [Test]
        public void ResolveAll_Dependency() {
            var container = new WindsorContainer();
            container.Kernel.DependencyResolving += Kernel_DependencyResolving;

            container.Register(Component.For(typeof (IIface<,>)).ImplementedBy(typeof (HandlerImpl<,>)));
            container.Register(Component.For<SomeService>());
            container.Kernel.Resolver.AddSubResolver(new ArrayResolver(container.Kernel));
            var s = container.Resolve<SomeService>();
        }

        private void Kernel_DependencyResolving(ComponentModel client, DependencyModel model, object dependency) {
            throw new NotImplementedException();
        }

        public class SomeService {
            public IIface<Impl1, Stub>[] s { get; set; }
        }

        public class Base {}

        public class Stub {}

        public interface AdditionalIface {}

        public interface IIface<T1, T2> where T1 : Base where T2 : class {
            T1 Command { get; set; }
        }

        public class HandlerImpl<T1, T2> : IIface<T1, T2> where T1 : Base, AdditionalIface where T2 : class {
            public T1 Command { get; set; }
        }

        public class Impl1 : Base {}

        public class Impl2 : Base, AdditionalIface {}
    }

    public static class WindsorExtensions {
        public static T[] TryResolveAll<T>(this IWindsorContainer container) {
            try {
                return container.ResolveAll<T>();
            } catch (ArgumentException) {
                return new T[0];
            }
        }
    }
}